{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Classifying Cars by Country (Pytorch)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Importing libraries"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 62,
   "metadata": {},
   "outputs": [],
   "source": [
    "import pandas as pd\n",
    "import numpy as np\n",
    "import torch\n",
    "from torch.nn import Sequential, Linear, ReLU, CrossEntropyLoss\n",
    "from torch.optim import SGD\n",
    "from torch.autograd import Variable\n",
    "from torch import Tensor\n",
    "from sklearn.model_selection import train_test_split\n",
    "from sklearn.preprocessing import MinMaxScaler\n",
    "import matplotlib.pyplot as plt\n",
    "import os\n",
    "os.chdir('C:/Users/Nicolas/Documents/Data/thecarconnection')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Importing the data"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "df = pd.read_csv('cleaned_encoded_data.csv')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We're only going to keep a few columns with continuous variables"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Index(['MSRP', 'Make', 'Model', 'Style Name', 'Passenger Capacity',\n",
       "       'Passenger Doors', 'Front Shoulder Room (in)', 'Front Head Room (in)',\n",
       "       'Second Leg Room (in)', 'Front Leg Room (in)',\n",
       "       'Second Shoulder Room (in)', 'Second Head Room (in)',\n",
       "       'Height, Overall (in)', 'Wheelbase (in)', 'Width, Max w/o mirrors (in)',\n",
       "       'Fuel Tank Capacity, Approx (gal)', 'EPA Fuel Economy Est - Hwy (MPG)',\n",
       "       'EPA Fuel Economy Est - City (MPG)', 'Third Gear Ratio (:1)',\n",
       "       'First Gear Ratio (:1)', 'Fourth Gear Ratio (:1)',\n",
       "       'Second Gear Ratio (:1)', 'Front Brake Rotor Diam x Thickness (in)',\n",
       "       'Rear Brake Rotor Diam x Thickness (in)',\n",
       "       'Turning Diameter - Curb to Curb', 'Basic Miles/km', 'Basic Years',\n",
       "       'Corrosion Miles/km', 'Corrosion Years', 'Drivetrain Miles/km',\n",
       "       'Drivetrain Years', 'Roadside Assistance Miles/km',\n",
       "       'Roadside Assistance Years', 'Hybrid Engine', 'Gears', 'Net Horsepower',\n",
       "       'Net Horsepower RPM', 'Net Torque', 'Net Torque RPM', 'Cylinders',\n",
       "       'Displacement (L)', 'Displacement (cc)', 'Rear Tire Width',\n",
       "       'Front Tire Width', 'Rear Wheel Size', 'Front Wheel Size',\n",
       "       'Tire Width Ratio', 'Wheel Size Ratio', 'Tire Ratio', 'Year',\n",
       "       'Country Code', 'Drivetrain: All Wheel Drive',\n",
       "       'Drivetrain: Four Wheel Drive', 'Drivetrain: Front Wheel Drive',\n",
       "       'Drivetrain: Rear Wheel Drive'],\n",
       "      dtype='object')"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "df.columns[:-220]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "The countries of origin are as follow: 17% from Germany, 30% from Japan, and 39% from the USA.\n"
     ]
    }
   ],
   "source": [
    "num_germany, num_japan, num_usa = df['Country: Germany'].sum()/df.shape[0], df['Country: Japan'].sum()/df.shape[0],\\\n",
    "    df['Country: USA'].sum()/df.shape[0]\n",
    "print('The countries of origin are as follow: {}% from Germany, {}% from Japan, and'.format(int(num_germany*100), \n",
    "                            int(num_japan*100)), '{}% from the USA.'.format(int(num_usa*100)))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "df = df[['MSRP', 'Front Shoulder Room (in)', 'Front Head Room (in)',\n",
    "       'Second Leg Room (in)', 'Front Leg Room (in)',\n",
    "       'Second Shoulder Room (in)', 'Second Head Room (in)',\n",
    "       'Height, Overall (in)', 'Wheelbase (in)', 'Width, Max w/o mirrors (in)',\n",
    "       'Fuel Tank Capacity, Approx (gal)', 'EPA Fuel Economy Est - Hwy (MPG)',\n",
    "       'EPA Fuel Economy Est - City (MPG)', 'Third Gear Ratio (:1)',\n",
    "       'First Gear Ratio (:1)', 'Fourth Gear Ratio (:1)',\n",
    "       'Second Gear Ratio (:1)', 'Front Brake Rotor Diam x Thickness (in)',\n",
    "       'Rear Brake Rotor Diam x Thickness (in)',\n",
    "       'Turning Diameter - Curb to Curb', 'Gears', 'Net Horsepower',\n",
    "       'Net Horsepower RPM', 'Net Torque', 'Net Torque RPM', \n",
    "       'Displacement (L)', 'Displacement (cc)', 'Rear Tire Width',\n",
    "       'Front Tire Width', 'Rear Wheel Size', 'Front Wheel Size', \n",
    "        'Country: Germany', 'Country: Japan', 'Country: USA']]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {
    "scrolled": true
   },
   "outputs": [],
   "source": [
    "df = df.loc[df.iloc[:, -3:].sum(axis=1) == 1] # keeping only German, Japanese, and American cars"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>MSRP</th>\n",
       "      <th>Front Shoulder Room (in)</th>\n",
       "      <th>Front Head Room (in)</th>\n",
       "      <th>Second Leg Room (in)</th>\n",
       "      <th>Front Leg Room (in)</th>\n",
       "      <th>Second Shoulder Room (in)</th>\n",
       "      <th>Second Head Room (in)</th>\n",
       "      <th>Height, Overall (in)</th>\n",
       "      <th>Wheelbase (in)</th>\n",
       "      <th>Width, Max w/o mirrors (in)</th>\n",
       "      <th>...</th>\n",
       "      <th>Net Torque RPM</th>\n",
       "      <th>Displacement (L)</th>\n",
       "      <th>Displacement (cc)</th>\n",
       "      <th>Rear Tire Width</th>\n",
       "      <th>Front Tire Width</th>\n",
       "      <th>Rear Wheel Size</th>\n",
       "      <th>Front Wheel Size</th>\n",
       "      <th>Country: Germany</th>\n",
       "      <th>Country: Japan</th>\n",
       "      <th>Country: USA</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>30034.0</td>\n",
       "      <td>65.2</td>\n",
       "      <td>41.0</td>\n",
       "      <td>33.7</td>\n",
       "      <td>41.30</td>\n",
       "      <td>66.3</td>\n",
       "      <td>38.4</td>\n",
       "      <td>76.2</td>\n",
       "      <td>143.5</td>\n",
       "      <td>79.7</td>\n",
       "      <td>...</td>\n",
       "      <td>4000.0</td>\n",
       "      <td>6.0</td>\n",
       "      <td>364.0</td>\n",
       "      <td>245.0</td>\n",
       "      <td>245.0</td>\n",
       "      <td>16.0</td>\n",
       "      <td>16.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>1.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>19515.0</td>\n",
       "      <td>54.5</td>\n",
       "      <td>39.2</td>\n",
       "      <td>39.1</td>\n",
       "      <td>42.40</td>\n",
       "      <td>40.0</td>\n",
       "      <td>33.2</td>\n",
       "      <td>66.3</td>\n",
       "      <td>125.7</td>\n",
       "      <td>69.4</td>\n",
       "      <td>...</td>\n",
       "      <td>3750.0</td>\n",
       "      <td>2.3</td>\n",
       "      <td>140.0</td>\n",
       "      <td>225.0</td>\n",
       "      <td>225.0</td>\n",
       "      <td>15.0</td>\n",
       "      <td>15.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>1.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>24595.0</td>\n",
       "      <td>56.3</td>\n",
       "      <td>40.8</td>\n",
       "      <td>37.8</td>\n",
       "      <td>43.00</td>\n",
       "      <td>56.1</td>\n",
       "      <td>39.3</td>\n",
       "      <td>65.7</td>\n",
       "      <td>107.9</td>\n",
       "      <td>71.7</td>\n",
       "      <td>...</td>\n",
       "      <td>4000.0</td>\n",
       "      <td>2.5</td>\n",
       "      <td>150.0</td>\n",
       "      <td>225.0</td>\n",
       "      <td>225.0</td>\n",
       "      <td>17.0</td>\n",
       "      <td>17.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>1.0</td>\n",
       "      <td>0.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>56600.0</td>\n",
       "      <td>58.9</td>\n",
       "      <td>39.6</td>\n",
       "      <td>36.0</td>\n",
       "      <td>41.86</td>\n",
       "      <td>56.7</td>\n",
       "      <td>38.9</td>\n",
       "      <td>67.4</td>\n",
       "      <td>114.0</td>\n",
       "      <td>76.3</td>\n",
       "      <td>...</td>\n",
       "      <td>1750.0</td>\n",
       "      <td>3.0</td>\n",
       "      <td>181.0</td>\n",
       "      <td>255.0</td>\n",
       "      <td>255.0</td>\n",
       "      <td>18.0</td>\n",
       "      <td>18.0</td>\n",
       "      <td>1.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>21795.0</td>\n",
       "      <td>57.5</td>\n",
       "      <td>40.1</td>\n",
       "      <td>39.3</td>\n",
       "      <td>41.00</td>\n",
       "      <td>55.5</td>\n",
       "      <td>39.0</td>\n",
       "      <td>65.7</td>\n",
       "      <td>106.3</td>\n",
       "      <td>72.4</td>\n",
       "      <td>...</td>\n",
       "      <td>4000.0</td>\n",
       "      <td>2.0</td>\n",
       "      <td>122.0</td>\n",
       "      <td>225.0</td>\n",
       "      <td>225.0</td>\n",
       "      <td>17.0</td>\n",
       "      <td>17.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>1.0</td>\n",
       "      <td>0.0</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "<p>5 rows × 34 columns</p>\n",
       "</div>"
      ],
      "text/plain": [
       "      MSRP  Front Shoulder Room (in)  Front Head Room (in)  \\\n",
       "0  30034.0                      65.2                  41.0   \n",
       "1  19515.0                      54.5                  39.2   \n",
       "2  24595.0                      56.3                  40.8   \n",
       "3  56600.0                      58.9                  39.6   \n",
       "4  21795.0                      57.5                  40.1   \n",
       "\n",
       "   Second Leg Room (in)  Front Leg Room (in)  Second Shoulder Room (in)  \\\n",
       "0                  33.7                41.30                       66.3   \n",
       "1                  39.1                42.40                       40.0   \n",
       "2                  37.8                43.00                       56.1   \n",
       "3                  36.0                41.86                       56.7   \n",
       "4                  39.3                41.00                       55.5   \n",
       "\n",
       "   Second Head Room (in)  Height, Overall (in)  Wheelbase (in)  \\\n",
       "0                   38.4                  76.2           143.5   \n",
       "1                   33.2                  66.3           125.7   \n",
       "2                   39.3                  65.7           107.9   \n",
       "3                   38.9                  67.4           114.0   \n",
       "4                   39.0                  65.7           106.3   \n",
       "\n",
       "   Width, Max w/o mirrors (in)      ...       Net Torque RPM  \\\n",
       "0                         79.7      ...               4000.0   \n",
       "1                         69.4      ...               3750.0   \n",
       "2                         71.7      ...               4000.0   \n",
       "3                         76.3      ...               1750.0   \n",
       "4                         72.4      ...               4000.0   \n",
       "\n",
       "   Displacement (L)  Displacement (cc)  Rear Tire Width  Front Tire Width  \\\n",
       "0               6.0              364.0            245.0             245.0   \n",
       "1               2.3              140.0            225.0             225.0   \n",
       "2               2.5              150.0            225.0             225.0   \n",
       "3               3.0              181.0            255.0             255.0   \n",
       "4               2.0              122.0            225.0             225.0   \n",
       "\n",
       "   Rear Wheel Size  Front Wheel Size  Country: Germany  Country: Japan  \\\n",
       "0             16.0              16.0               0.0             0.0   \n",
       "1             15.0              15.0               0.0             0.0   \n",
       "2             17.0              17.0               0.0             1.0   \n",
       "3             18.0              18.0               1.0             0.0   \n",
       "4             17.0              17.0               0.0             1.0   \n",
       "\n",
       "   Country: USA  \n",
       "0           1.0  \n",
       "1           1.0  \n",
       "2           0.0  \n",
       "3           0.0  \n",
       "4           0.0  \n",
       "\n",
       "[5 rows x 34 columns]"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "df.head()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Separating `X` and `y`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [],
   "source": [
    "X = df.iloc[:, :-3].values.astype(np.float64)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [],
   "source": [
    "y = df.iloc[:, -3:].values.astype(np.float64)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [],
   "source": [
    "assert X.shape[0] == y.shape[0]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Scaling the data"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [],
   "source": [
    "scaler = MinMaxScaler()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [],
   "source": [
    "X = scaler.fit_transform(X)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[0., 0., 1.],\n",
       "       [0., 0., 1.],\n",
       "       [0., 1., 0.],\n",
       "       [1., 0., 0.],\n",
       "       [0., 1., 0.],\n",
       "       [0., 0., 1.],\n",
       "       [0., 0., 1.],\n",
       "       [0., 1., 0.],\n",
       "       [0., 1., 0.],\n",
       "       [0., 0., 1.]])"
      ]
     },
     "execution_count": 23,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "y[:10] "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([2, 2, 1, 0, 1, 2, 2, 1, 1, 2], dtype=int64)"
      ]
     },
     "execution_count": 24,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "np.where(y == 1)[1][:10]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {},
   "outputs": [],
   "source": [
    "y = np.where(y == 1)[1].astype(np.float64)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Train test split"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "metadata": {},
   "outputs": [],
   "source": [
    "x_train, x_test, y_train, y_test = train_test_split(X, y, test_size=0.2)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Prediction "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 65,
   "metadata": {},
   "outputs": [],
   "source": [
    "hidden_units = 64"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 66,
   "metadata": {},
   "outputs": [],
   "source": [
    "model = Sequential(\n",
    "    Linear(31, 64),\n",
    "    ReLU(),\n",
    "    Linear(64, 128),\n",
    "    ReLU(),\n",
    "    Linear(128, 3)\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 67,
   "metadata": {},
   "outputs": [],
   "source": [
    "criterion = CrossEntropyLoss()\n",
    "optimizer = SGD(model.parameters(), lr=1e-1, momentum=9e-1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 68,
   "metadata": {},
   "outputs": [],
   "source": [
    "epochs = 1_000"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 69,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Epoch 1 Loss: 1.0874\n",
      "Epoch 100 Loss: 0.5189\n",
      "Epoch 200 Loss: 0.4697\n",
      "Epoch 300 Loss: 0.2880\n",
      "Epoch 400 Loss: 0.6979\n",
      "Epoch 500 Loss: 0.2616\n",
      "Epoch 600 Loss: 0.2342\n",
      "Epoch 700 Loss: 0.2870\n",
      "Epoch 800 Loss: 0.1909\n",
      "Epoch 900 Loss: 0.1532\n",
      "Epoch 1000 Loss: 0.1395\n"
     ]
    }
   ],
   "source": [
    "loss_history = []\n",
    "for epoch in range(epochs):\n",
    "    inx = np.random.randint(0, x_train.shape[0], 24_000)\n",
    "    inputs = Variable(Tensor(x_train[inx]).float())\n",
    "    targets = Variable(Tensor(y_train[inx]).long())\n",
    "    optimizer.zero_grad()\n",
    "    out = model(inputs)\n",
    "    loss = criterion(out, targets)\n",
    "    loss.backward()\n",
    "    optimizer.step()\n",
    "    loss_history.append(loss.item())\n",
    "    \n",
    "    if epoch == 0 or (epoch + 1) % 100 == 0:\n",
    "        print('Epoch %d Loss: %.4f' % (epoch + 1, loss.item()))    "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 76,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "fig, ax = plt.subplots()\n",
    "ax.plot(np.arange(1, len(loss_history)+1), loss_history)\n",
    "_ = plt.title('Loss History')\n",
    "_ = plt.ylabel('Loss')\n",
    "_ = plt.xlabel('Epoch')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Test accuracy"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 71,
   "metadata": {},
   "outputs": [],
   "source": [
    "inputs = Variable(Tensor(x_test).float())\n",
    "targets = Variable(Tensor(y_test).long())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 72,
   "metadata": {},
   "outputs": [],
   "source": [
    "optimizer.zero_grad()\n",
    "out = model(inputs)\n",
    "_, predicted = torch.max(out.data, 1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 73,
   "metadata": {},
   "outputs": [],
   "source": [
    "error_count = y_test.size - np.count_nonzero((targets == predicted).numpy())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 74,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Errors: 276 — Accuracy: 94%\n"
     ]
    }
   ],
   "source": [
    "print('Errors: %d — Accuracy: %d%%' % (error_count, 100*torch.sum(targets == predicted)/y_test.size))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 75,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Using 1/6th of the features, we can classify cars according to their country with 94% accuracy!\n"
     ]
    }
   ],
   "source": [
    "print('Using 1/6th of the features, we can classify cars according to their',\n",
    "     'country with {}% accuracy!'.format(100*torch.sum(targets == predicted)/y_test.size))"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.0"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
